package waal.pc.frontend;

import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import java.io.File;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.border.BevelBorder;
import javax.swing.border.Border;

import waal.pc.backend.Log;
import waal.pc.backend.Utility;
import waal.pc.backend.hub.Hub;
import waal.pc.backend.hub.HubEvent;
import waal.pc.backend.hub.HubEventListener;
import waal.pc.backend.module.Module;
import waal.pc.process.DialogFileChooser;
import waal.pc.process.FileChooserFileFilter;
import waal.pc.process.XMLProcess;

public class ProcessTab implements HubEventListener {

	private Surface surface;
	private JPanel panel;
	private GridBagLayout tgbl, pgbl;
	private JTextField fileField;
	private JComboBox<Hub> hubs;
	private JLabel processNameLabel;
	private JLabel processComentLabel;
	private JLabel processTimbaseLabelLabel;
	private int row = 0;
	private JPanel processScreen;
	private long timebase;
	private List<int[]> process;
	private int processSize = 0;
	private int idCounter = 0;
	private int lastTime = 0;
	private boolean processGood = false;	
	private Map<Integer, String> digitalCommand;
	private Map<Integer, String> analogCommand;
	private Map<Integer, String> configCommand;
	private JLabel processGoodLabel;
	private DialogFileChooser fc;
	private String defaultFile = "P:\\coffee\\Waaal\\Process";
	private File processFile = null;
	private JButton readButton;
	private JButton sendButton;
	private JButton startButton;
	private HashMap<Integer, String> virtualCommand;
	
	private static Border border = new BevelBorder(BevelBorder.RAISED);
	
	@SuppressWarnings("unused")
	private static int NONE = GridBagConstraints.NONE;
	@SuppressWarnings("unused")
	private static int HORIZONTAL = GridBagConstraints.HORIZONTAL;
	@SuppressWarnings("unused")
	private static int VERTICAL = GridBagConstraints.VERTICAL;
	private static int BOTH = GridBagConstraints.BOTH;
	
	public ProcessTab(Surface surface) {
		this.surface = surface;
		this.fillCommandTextMaps();
		this.process = new LinkedList<int[]>();
		this.panel = new JPanel();
		this.tgbl = new GridBagLayout();
		this.panel.setLayout(this.tgbl);
		this.fc = new DialogFileChooser(this.defaultFile);
		this.fc.addFileExtension(new FileChooserFileFilter("xml"));
		this.hubs = new JComboBox<Hub>();
		this.fillHubList();
		this.surface.addHubEventListener(this);
		this.fileField = new JTextField(30);
		JButton fileButton = new JButton("Select");
		this.readButton = new JButton("Read File");
		this.readButton.setEnabled(false);
		this.sendButton = new JButton("Send Process");
		this.sendButton.setEnabled(false);
		this.startButton = new JButton("Start");
		this.startButton.setEnabled(false);
		
		ActionListener fileButtonAction = new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				processFile = fc.getOpenFile();
				fileField.setText(processFile.toString());
				readButton.setEnabled(true);
			}
		};

		ActionListener readButtonAction = new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				startXMLReader();				
			}
		};
		
		ActionListener sendButtonAction = new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				sendProcess();					
			}
		};
		
		fileButton.addActionListener(fileButtonAction);
		this.readButton.addActionListener(readButtonAction);
		this.sendButton.addActionListener(sendButtonAction);
		
		
		this.processNameLabel = new JLabel("");
		this.processComentLabel = new JLabel("");
		this.processTimbaseLabelLabel = new JLabel("");
		this.processGoodLabel = new JLabel("" + this.processGood);
		
		//															x, y, w, h, wx, wy
		this.addTopComponent(new JLabel("Process"), 				0, 0, 6, 1, 1, 0);
		this.addTopComponent(new JLabel("Process File:"), 			0, 1, 1, 1, 0, 0);
		this.addTopComponent(this.fileField,						1, 1, 3, 1, 0, 0);
		this.addTopComponent(fileButton,							4, 1, 1, 1, 0, 0);
		this.addTopComponent(new JLabel(),							5, 1, 1, 1, 1, 0);
		this.addTopComponent(readButton,							0, 2, 1, 1, 0, 0);
		this.addTopComponent(sendButton,							1, 2, 1, 1, 0, 0);
		this.addTopComponent(startButton,							2, 2, 1, 1, 0, 0);
		this.addTopComponent(new JLabel(),							3, 2, 3, 1, 1, 0);
		this.addTopComponent(new JLabel("Process Information:"),	0, 3, 6, 1, 1, 0);
		this.addTopComponent(new JLabel("Hub:"),					0, 4, 1, 1, 1, 0);
		this.addTopComponent(this.hubs,								1, 4, 3, 1, 1, 0);
		this.addTopComponent(new JLabel(),							4, 4, 2, 1, 1, 0);
		this.addTopComponent(new JLabel("Name:"),					0, 5, 1, 1, 0, 0);
		this.addTopComponent(this.processNameLabel,					1, 5, 5, 1, 1, 0);
		this.addTopComponent(new JLabel("Beschreibung:"),			0, 6, 1, 1, 0, 0);
		this.addTopComponent(this.processComentLabel,				1, 6, 5, 1, 1, 0);
		this.addTopComponent(new JLabel("Timebase:"),				0, 7, 1, 1, 0, 0);
		this.addTopComponent(this.processTimbaseLabelLabel,			1, 7, 5, 1, 1, 0);
		this.addTopComponent(new JLabel("Process good:"),			0, 8, 1, 1, 0, 0);
		this.addTopComponent(this.processGoodLabel,					1, 8, 5, 1, 1, 0);
		
		this.pgbl = new GridBagLayout();
		this.processScreen = new JPanel(this.pgbl);
		this.addTopComponent(this.processScreen,					0, 9, 6, 1, 1, 1);
		
		
	}
	
	public JScrollPane getPanel() {
		return new JScrollPane(this.panel);
	}
	
	private void fillHubList() {
		this.hubs.removeAll();
		Log.addToDebug(2, "ProcessTab", "fillHubList", "found " + this.surface.getHubs().size() + " Hubs");
		for(Iterator<Hub> ih = this.surface.getHubs().iterator(); ih.hasNext(); ) {
			Hub hub = ih.next();
			if(hub.isMaster()) {
				this.hubs.addItem(hub);
			}
		}
		
	}
	
	private void addTopComponent(JComponent c, int x, int y, int width, int height, double weightx, double weighty ) {
		GridBagConstraints gbc = new GridBagConstraints();
		gbc.fill = GridBagConstraints.BOTH;
		gbc.gridx = x; gbc.gridy = y;
		gbc.gridwidth = width; gbc.gridheight = height;
		gbc.weightx = weightx; gbc.weighty = weighty;
		gbc.ipadx = 5; gbc.ipady = 5;
		//c.setBorder(ProcessTab.border);
		this.tgbl.setConstraints( c, gbc );
		this.panel.add( c );
	}
	
	private void addProcessComponent(JComponent c, int x, int y, int width, int height, double weightx, double weighty, int fill, boolean border, int location) {
		GridBagConstraints gbc = new GridBagConstraints(x, y,width, height, weightx, weighty, location, fill, new Insets(0,0,0,0), 20, 10);
		if(border) {
			c.setBorder(ProcessTab.border);
		}
		c.setAlignmentX(0.5f);
		
		this.pgbl.setConstraints( c, gbc );
		this.processScreen.add(c);
	}
	
	private void addProcessComponent(JComponent c, int x, int y, int width, int height, double weightx, double weighty, int fill, Color col) {
		this.addProcessComponent(c, x, y, width, height, weightx, weighty, fill);
		c.setOpaque(true);
		c.setBackground(col);
	}
	
	private void addProcessComponent(JComponent c, int x, int y, int width, int height, double weightx, double weighty, int fill) {
		this.addProcessComponent(c, x, y, width, height, weightx, weighty, fill, true, GridBagConstraints.WEST);
	}
	
	public void clearProcess() {
		this.processScreen.removeAll();
		this.processSize = 0;
		this.process.clear();
		this.idCounter = 0;
		this.processGood = false;
		this.lastTime = 0;
		int loc = SwingConstants.CENTER;
		this.addProcessComponent(new JLabel("ID", loc), 			 0, 0, 1, 1, 0, 0, ProcessTab.BOTH, true, GridBagConstraints.CENTER);
		this.addProcessComponent(new JLabel("Time", loc),			 1, 0, 1, 1, 0, 0, ProcessTab.BOTH, true, GridBagConstraints.CENTER);
		this.addProcessComponent(new JLabel("Type", loc),			 2, 0, 1, 1, 0, 0, ProcessTab.BOTH, true, GridBagConstraints.CENTER);
		this.addProcessComponent(new JLabel("Command", loc),		 3, 0, 1, 1, 0, 0, ProcessTab.BOTH, true, GridBagConstraints.CENTER);
		this.addProcessComponent(new JLabel("Command Text", loc),	 4, 0, 1, 1, 0, 0, ProcessTab.BOTH, true, GridBagConstraints.CENTER);
		this.addProcessComponent(new JLabel("Module", loc), 		 5, 0, 1, 1, 0, 0, ProcessTab.BOTH, true, GridBagConstraints.CENTER);
		this.addProcessComponent(new JLabel("Channel", loc),		 6, 0, 1, 1, 0, 0, ProcessTab.BOTH, true, GridBagConstraints.CENTER);
		this.addProcessComponent(new JLabel("Para. 1", loc),		 7, 0, 1, 1, 0, 0, ProcessTab.BOTH, true, GridBagConstraints.CENTER);
		this.addProcessComponent(new JLabel("Para. 2", loc),		 8, 0, 1, 1, 0, 0, ProcessTab.BOTH, true, GridBagConstraints.CENTER);
		this.addProcessComponent(new JLabel("Para. 3", loc),		 9, 0, 1, 1, 0, 0, ProcessTab.BOTH, true, GridBagConstraints.CENTER);
		this.addProcessComponent(new JLabel("Para. 4", loc),		10, 0, 1, 1, 0, 0, ProcessTab.BOTH, true, GridBagConstraints.CENTER);
		this.addProcessComponent(new JLabel("Para. 5", loc),		11, 0, 1, 1, 0, 0, ProcessTab.BOTH, true, GridBagConstraints.CENTER);
		this.addProcessComponent(new JLabel("Para. 6", loc),		12, 0, 1, 1, 0, 0, ProcessTab.BOTH, true, GridBagConstraints.CENTER);
		this.addProcessComponent(new JLabel(""),					13, 0, 1, 1, 1, 0, ProcessTab.BOTH, false, GridBagConstraints.CENTER);
		this.row = 1;
	}
	
	public void addModuelLine(int time, int module, int channel, int command, int type, String comment) {
		this.addModuelLine(time, module, channel, command, type, comment, null);		
	}
	
	public void addModuelLine(int time, int module, int channel, int command, int type, String comment, int[] parameter) {
		boolean good;
		Color col;
		int id, mcommand;
		
		if(time >= this.lastTime) {
			good = true;
			id = this.idCounter++;
			this.lastTime = time;
			col = new Color(175,245,150);
		} else {
			good = false;
			id = -1;
			col = new Color(255,145,145);
		}
		
		
		this.changeGoodStatus(good);
		
		JLabel commandLabel = new JLabel("0x" + Integer.toHexString(command));
		commandLabel.setToolTipText("Dec.: " + command);
		
		String typeName, commandText;
		if(type == 0) {
			typeName = "anal.";
			mcommand = 0xcf;
			commandText = this.analogCommand.get(command);
		} else if(type == 1) {
			typeName = "dig.";
			mcommand = 0xdf;
			commandText = this.digitalCommand.get(command);
		} else {
			typeName = "virt.";
			mcommand = 0xaf;
			commandText = this.virtualCommand.get(command);
		}
		if(commandText == null) commandText = "Error";
		
		JLabel commandTextLabel = new JLabel(commandText);
		if(comment != null) {
			commandTextLabel.setToolTipText(comment);
		}
		
		if(good) {
			int paraSize;
			if(parameter != null) {
				paraSize = parameter.length;
			} else {
				paraSize = 0;
			}
			int[] step = new int[11 + paraSize];
			step[0] = (id & 0xFF);
			step[1] = ((id & 0xFF00) >> 8);
			step[2] = ((time & 0x000000FF));
			step[3] = ((time & 0x0000FF00) >> 8);
			step[4] = ((time & 0x00FF0000) >> 16);
			step[5] = ((time & 0xFF000000) >> 24);
			step[6] = (4 + paraSize);
			step[7] = mcommand;
			step[8] = module;
			step[9] = channel;
			step[10] = command;
			for(int c = 0; c < paraSize; c++ ){
				step[11+c] = parameter[c];
			}
			this.processSize += step.length;
			this.process.add(step);
			//Log.addToDebug(2, "ProcessTab", "addModuelLine", "add a step id: " + id  + " time: " + time + " mc: " + mcommand + " command: " + command + " moduel: " + module + " channel: " + channel + " Parmeters: " + paraSize);
			/*StringBuffer debug = new StringBuffer();
			int c = 0;
			for(int b = 0; b< step.length; b++ ) {
				debug.append("[" + b + "] -> " + step[b] + "(0x" + Integer.toHexString(step[b]) + ")\t");
				c++;
				if(c == 4) {
					debug.append("\n");
					c = 0;
				}
			}
			Log.addToDebug(2, "ProcessTab", "addModuelLine", "Step: " + debug);
			*/
		}
		
		Hub hub = (Hub) this.hubs.getSelectedItem();
		JLabel moduleLabel = new JLabel("" + module);
		JLabel channelLabel = new JLabel("" + channel);
		if(hub != null) {
			Module myModule = hub.getModule(module);
			if(myModule != null){
				moduleLabel.setToolTipText(myModule.getName());
				if(channel > 0) {
					channelLabel.setToolTipText(myModule.getChannel(channel-1).toString());
				} else {
					channelLabel.setToolTipText("all");
				}
			}
		}
		
		if(parameter == null) {
			this.addProcessLine(col, "" + id, "" + time, typeName, commandLabel, commandTextLabel, moduleLabel, channelLabel, new JLabel(), new JLabel(), new JLabel(), new JLabel(), new JLabel(), new JLabel());
		} else {
			int i;
			JLabel[] para = new JLabel[6];
			for(i = 0; i < parameter.length; i++) {
				para[i] = new JLabel(parameter[i] + "");
				para[i].setToolTipText("'" +Integer.toBinaryString(parameter[i]) + "' 0x" + Integer.toHexString(parameter[i]));
			}
			for( ; i < para.length; i++) {
				para[i] = new JLabel();
			}
			this.addProcessLine(col, "" + id, "" + time, typeName, commandLabel, commandTextLabel, moduleLabel, channelLabel, para[0], para[1], para[2], para[3], para[4], para[5]);
		}
		
	}
	
	public void addConfigLine(int time, int command, String comment) {
		this.addConfigLine(time, command, comment, null);
	}
	
	public void addConfigLine( int time, int command, String comment, int[] parameter) {
		boolean good;
		Color col;
		int id;
		if(time >= this.lastTime) {
			good = true;
			id = this.idCounter++;
			col = new Color(175,245,150);
			this.lastTime = time;
		} else {
			good = false;
			col = new Color(255,145,145);
			id = -1;
		}
		this.changeGoodStatus(good);
		
		JLabel commandLabel = new JLabel("0x" + Integer.toHexString(command));
		commandLabel.setToolTipText("Dec.: " + command);
		
		String commandText = this.configCommand.get(command);
		if(commandText == null) commandText = "Error";
		JLabel commandTextLabel = new JLabel(commandText);
		if(comment != null) {
			commandTextLabel.setToolTipText(comment);
		}
			
		if(good) {
			int paraSize;
			if(parameter != null) {
				paraSize = parameter.length;
			} else {
				paraSize = 0;
			}
			int[] step = new int[11 + paraSize];
			step[0] = (id & 0xFF);
			step[1] = ((id & 0xFF00) >> 8);
			step[2] = ((time & 0x000000FF));
			step[3] = ((time & 0x0000FF00) >> 8);
			step[4] = ((time & 0x00FF0000) >> 16);
			step[5] = ((time & 0xFF000000) >> 24);
			step[6] = 1 +  paraSize;
			step[7] = command;
			for(int c = 0; c < paraSize; c++ ){
				step[8+c] = parameter[c];
			}
			this.processSize += step.length;
			this.process.add(step);
			Log.addToDebug(2, "ProcessTab", "addModuelLine", "add a step id: " + id  + " time: " + time + " command: " + command + " Parmeters: " + paraSize);
		}
		
		
		if(parameter == null) {
			this.addProcessLine(col, "" + id, "" + time, "Conf.", commandLabel, commandTextLabel, new JLabel(), new JLabel(), new JLabel(), new JLabel(), new JLabel(), new JLabel(), new JLabel(), new JLabel());
		} else {
			int i;
			JLabel[] para = new JLabel[6];
			for(i = 0; i < parameter.length; i++) {
				para[i] = new JLabel(parameter[i] + "");
				para[i].setToolTipText("'" +Integer.toBinaryString(parameter[i]) + "' 0x" + Integer.toHexString(parameter[i]));
			}
			for( ; i < para.length; i++) {
				para[i] = new JLabel();
			}
			this.addProcessLine(col, "" + id, "" + time, "Conf.", commandLabel, commandTextLabel, new JLabel(), new JLabel(), para[0], para[1], para[2], para[3], para[4], para[5]);
		}
		
	}
	
	private void addProcessLine(Color col, String id, String time, String type, JLabel command, JLabel commandText, JLabel module, JLabel channel, 
								JLabel para1, JLabel para2, JLabel para3, JLabel para4, JLabel para5, JLabel para6) {
				
		this.addProcessComponent(new JLabel(id),			 0, row, 1, 1, 0, 0, ProcessTab.BOTH, col);
		this.addProcessComponent(new JLabel(time),		 	 1, row, 1, 1, 0, 0, ProcessTab.BOTH, col);
		this.addProcessComponent(new JLabel(type),	 	 	 2, row, 1, 1, 0, 0, ProcessTab.BOTH, col);
		this.addProcessComponent(command,	 				 3, row, 1, 1, 0, 0, ProcessTab.BOTH, col);
		this.addProcessComponent(commandText,				 4, row, 1, 1, 0, 0, ProcessTab.BOTH, col);
		this.addProcessComponent(module,					 5, row, 1, 1, 0, 0, ProcessTab.BOTH, col);
		this.addProcessComponent(channel,					 6, row, 1, 1, 0, 0, ProcessTab.BOTH, col);
		this.addProcessComponent(para1,						 7, row, 1, 1, 0, 0, ProcessTab.BOTH, col);
		this.addProcessComponent(para2,						 8, row, 1, 1, 0, 0, ProcessTab.BOTH, col);
		this.addProcessComponent(para3,						 9, row, 1, 1, 0, 0, ProcessTab.BOTH, col);
		this.addProcessComponent(para4,						10, row, 1, 1, 0, 0, ProcessTab.BOTH, col);
		this.addProcessComponent(para5,						11, row, 1, 1, 0, 0, ProcessTab.BOTH, col);
		this.addProcessComponent(para6,						12, row, 1, 1, 0, 0, ProcessTab.BOTH, col);
		this.addProcessComponent(new JLabel(""),			13, row, 1, 1, 1, 0, ProcessTab.BOTH, false, GridBagConstraints.CENTER);
		this.row++;
	}
	
	public void setDescription(String name, String coment) {
		this.processNameLabel.setText(name);
		if(coment != null) {
			this.processComentLabel.setText(coment);
		} else {
			this.processComentLabel.setText("");
		}
	}
	
	public void setTimebase(long timebase, String value, String unit) {
		this.timebase = timebase;
		this.processTimbaseLabelLabel.setText(value + unit);		
	}
	
	private void changeGoodStatus(boolean value) {
		if(value != this.processGood) {
			this.processGood = value;
			this.processGoodLabel.setText("" + value);
			this.sendButton.setEnabled(value);
			this.startButton.setEnabled(value);
		}
	}
	
	private void fillCommandTextMaps() {
		this.analogCommand = new HashMap<Integer, String>();
		this.digitalCommand = new HashMap<Integer, String>();
		this.configCommand = new HashMap<Integer, String>();
		this.virtualCommand = new HashMap<Integer, String>();
		
		this.analogCommand.put(0xFE, "Set Output (16 Bit)");
		
		this.digitalCommand.put(0xFD, "Set Output to Pin");
		this.digitalCommand.put(0xFB, "Set Output Bitpattern");
		
		this.configCommand.put(0xEE, "Measurment start");
		this.configCommand.put(0xED, "Measurment stop");
		
		this.virtualCommand.put(0xFF, "Set function on/off");
		this.virtualCommand.put(0xFE, "If-Then conditoin");
		this.virtualCommand.put(0xFD, "mean value");
	}
	
	private void startXMLReader(){
		if(this.processFile != null) {
			this.clearProcess();
			new XMLProcess(this, this.processFile);
			
		} else {
			
		}
	}
	
	public void xmlProcessEnd() {
		this.addProcessComponent(new JLabel(""), 0, row, 14, 1, 1, 1, BOTH);
		this.processScreen.repaint();
		this.panel.revalidate();
	}
	
	public void sendProcess() {
		Hub hub = (Hub) this.hubs.getSelectedItem();
		hub.sendTimeBase(this.timebase);
		int[] dest = new int[this.processSize+1];
		int[] src;
		dest[0] = (byte) 0xBE;
		int i = 1;
		for(Iterator<int[]> is = this.process.iterator(); is.hasNext(); ){
			src = is.next();
			System.arraycopy(src, 0, dest, i, src.length);
			i = i + src.length;
		}
		hub.sendProcess(Utility.intAToByteAcs(dest));
		
	}

	
	@Override
	public void actionHubEvent(HubEvent e) {
		if(e.getEvent() == 6) { // Owner change
			DefaultComboBoxModel<Hub> m = (DefaultComboBoxModel<Hub>) this.hubs.getModel();
			
			if(e.getHub().isMaster()) {
				for(int i = 1; i < m.getSize(); i++) {
					if(m.getElementAt(i).equals(e.getHub())) {
						m.removeElementAt(i);
						return;
					}
				}
				m.addElement(e.getHub());
			} else {
				for(int i = 1; i < m.getSize(); i++) {
					if(m.getElementAt(i).equals(e.getHub())) {
						m.removeElementAt(i);
						return;
					}
				}
			}
			
		}
		
	}

	
}
